var utils = require('common/utils');

module.exports = VirtualList;

/**
   * @description better performance list by limit amount of list item
   * @param {string} el
   * @param {array} listData
   * @param {function} creatItemCallback
   * @param {object} options

   * @example
   * new VirtualList('body', [], function(){
   *   return document.create('span')
   * }, {})
   */

function VirtualList(el, listData, creatItemCallback, options) {
  this.elString = el;
  this.el = document.querySelector(el); // 容器元素
  this.listData = listData; // 列表数据
  this.creatItemCallback = creatItemCallback; // 生成列表DOM元素的回调函数
  // option 配置参考
  this.__defaultOptions = {
    // 默认参数配置
    listHeight: 50,
    dynamicHeight: false,
    estimateListHeight: 50,
    bufferScale: 0.5,
    throttleTime: 100,
    // useFrameOptimize: false
  };
  this.previousEndIndex = 0;
  if (!options) options = {};
  this.inputCheck(options); // 输入安全检查
  this.options = utils.mergeObj(Object.create(null), this.__defaultOptions, options); // 执行配置对象混入
  this.initPositionsInfo(); // 动态高度模式下初始化位置信息
  this.initBase(); // 初始化基本参数
  this.initContainer();
  this.bindEvents();
  this.render(0); // 起始渲染
}
// 类型输入检查
VirtualList.prototype.inputCheck = function (option) {
  if (!this.el || !this.listData || !this.creatItemCallback) {
    throw new Error('参数 el, listData, creatItemCallback 是必传参数');
  }
  if (!(this.el instanceof HTMLElement)) {
    throw new Error('没有输入或者选择器捕捉到的DOM元素');
  }
  if (!utils.isArray(this.listData)) {
    throw new Error('参数 listData 必须是数组类型');
  }
  if (!utils.isFunction(this.creatItemCallback)) {
    throw new Error('参数 creatItemCallback 必须是函数类型');
  }
  if (!utils.isObject(option)) {
    console.warn('没有显式的指定配置参数，将使用默认配置。');
  }
  this.optionCheck(option);
};
// 输入配置参数检查
VirtualList.prototype.optionCheck = function (option) {
  if (!option.dynamicHeight) {
    if (!option.listHeight) {
      console.warn('未显示指定元素的固定高度，将采用高度默认参数。');
    }
  } else {
    if (option.estimateListHeight && !utils.isNumber(option.estimateListHeight)) {
      throw new Error('配置的预估高度应该是一个number类型，');
    } else {
      if (option.estimateListHeight < 25) {
        console.warn('动态预估高度的参数小于25px，可能会渲染多余的DOM元素造成性能瓶颈。');
      }
    }
  }
};
// 判断是否处于动态加载模式，若是初始化加载位置信息数据
VirtualList.prototype.initPositionsInfo = function () {
  if (this.options.dynamicHeight === true) {
    var index = 0; // 元素索引
    this.listPositionsInfo = [];
    var estimateListHeight = this.options.estimateListHeight;
    // 硬加载
    while (index < this.listData.length) {
      // 初始化位置信息参数
      this.listPositionsInfo.push({
        index,
        height: estimateListHeight,
        top: index * estimateListHeight,
        bottom: (index + 1) * estimateListHeight,
      });
      index++;
    }
  }
};
// 初始化基本参数
VirtualList.prototype.initBase = function () {
  var listHeight = this.options.listHeight;
  var listData = this.listData;

  console.log('listPositionsInfo', this.listPositionsInfo);
  this.containerClientHeight = this.el.clientHeight; // 可视区域的高度
  var divideNum =
    this.options.dynamicHeight === true
      ? parseInt(this.options.estimateListHeight, 10)
      : parseInt(this.options.listHeight, 10);
  // 计算可视区域的渲染数量-->不含缓冲区
  this.clientListAmounts = Math.ceil(
    parseInt(this.containerClientHeight, 10) / divideNum,
  );
  // 计算列表总高度
  this.totalHeight =
    this.options.dynamicHeight && this.listPositionsInfo && this.listPositionsInfo.length
      ? (this.listPositionsInfo[this.clientListAmounts] &&
          this.listPositionsInfo[this.clientListAmounts].bottom) ||
        0
      : listHeight * listData.length; // 静态高度下采用配置高度
};
// 初始化容器元素
VirtualList.prototype.initContainer = function () {
  this.el.style.overflow = 'auto';
  this.el.style.position = 'relative';
  // 设置遮罩元素
  if (document.querySelector(this.elString + ' .mask-container')) {
    this.el.removeChild(document.querySelector(this.elString + ' .mask-container'));
  }

  var node = document.createElement('div');
  node.style.position = 'absolute';
  node.style.height = this.totalHeight - 800 + 'px';
  // maskContainer.style.zIndex = -1;
  node.style.top = '0';
  node.style.right = '0';
  node.style.left = '0';

  var maskContainer = node.cloneNode(false);

  maskContainer.style.zIndex = -1;
  maskContainer.setAttribute('class', 'mask-container');
  this.el.appendChild(maskContainer);
  this.__maskContainer = maskContainer;
  // 设置偏移容器
  if (document.querySelector(this.elString + ' .offset-container')) {
    this.el.removeChild(document.querySelector(this.elString + ' .offset-container'));
  }
  var offsetContainer = node.cloneNode(false);
  offsetContainer.setAttribute('class', 'offset-container');
  this.el.appendChild(offsetContainer);
  this.__offsetContainer = offsetContainer; // 设置引用
};
// 绑定监听方法
VirtualList.prototype.bindEvents = function () {
  var startY = 0;
  var _this = this;
  // 闭包的方式访问位置
  var updateOffset = function (e) {
    e.preventDefault();
    startY = e.target.scrollTop;
    _this.render(startY);
  };
  var throttleUpdateOffset = utils.throttle(updateOffset, _this.options.throttleTime);

  this.el.addEventListener('scroll', throttleUpdateOffset);
};
// 找到首项数据索引
VirtualList.prototype.findFirstIndex = function (offset) {
  if (this.options.dynamicHeight == true) {
    return utils.binarySearch(this.listPositionsInfo, offset); // 二分搜索
  } else {
    var listHeight = this.options.listHeight;
    return Math.floor(offset / listHeight);
  }
};
// 找到末项数据索引
VirtualList.prototype.findLastIndex = function (startIndex) {
  return startIndex + this.clientListAmounts;
};
// 计算上半区域缓存开始坐标
VirtualList.prototype.calAboveBuffer = function (startIndex, renderCount) {
  var bufferScale = this.options.bufferScale;
  var index;
  if (typeof bufferScale === 'number') {
    index = startIndex - Math.ceil(renderCount * bufferScale);
  } else if (Object.prototype.toString.call(bufferScale) === '[object Array]') {
    index = startIndex - Math.ceil(renderCount * bufferScale[0]);
  } else {
    console.warn('请检查输入的缓冲区数据类型是否正确。');
    return;
  }
  return index < 0 ? 0 : index;
};
// 计算下半区域缓存结束坐标
VirtualList.prototype.calBelowBuffer = function (endIndex, renderCount) {
  var bufferScale = this.options.bufferScale;
  var index;
  if (typeof bufferScale === 'number') {
    index = endIndex + Math.ceil(renderCount * bufferScale);
  } else if (Object.prototype.toString.call(bufferScale) === '[object Array]') {
    index = endIndex + Math.ceil(renderCount * bufferScale[1]);
  } else {
    console.warn('请检查输入的缓冲区数据类型是否正确。');
    return;
  }
  return index > this.listData.length ? this.listData.length : index;
};
// 渲染列表方法
VirtualList.prototype.render = function (offset) {
  var startIndex = this.findFirstIndex(offset);
  var endIndex = Math.min(this.findLastIndex(startIndex), this.listData.length);
  if (this.options.bufferScale) {
    // 如果存在缓冲区
    startIndex = this.calAboveBuffer(startIndex, this.clientListAmounts);
    endIndex = this.calBelowBuffer(endIndex, this.clientListAmounts);
  }
  if (endIndex < this.listData.length) {
    this.previousEndIndex = endIndex;
  }
  console.log('endIndex', endIndex, this.listData.length);
  if (this.previousEndIndex === this.listData.length) {
    this.options.renderedCallback && this.options.renderedCallback();
    return;
  }
  this.renderList = this.listData.slice(startIndex, endIndex);
  var _this = this;
  var htmlStr = this.renderList.map(__renderCallback).join('');
  this.__offsetContainer.innerHTML = htmlStr;
  this.options.renderedCallback && this.options.renderedCallback();
  if (endIndex >= this.listData.length) {
    this.previousEndIndex = endIndex;
  }

  // 渲染回调函数--->普通模式
  function __renderCallback(data) {
    var item = _this.creatItemCallback(data);

    if (!_this.options.dynamicHeight) item.style.height = _this.options.listHeight + 'px';
    return item.outerHTML;
  }
  // 若为动态高度模式，更新位置信息
  if (this.options.dynamicHeight === true) {
    this.updatelistPositionInfo(this.__offsetContainer.children, startIndex);
    this.updateTotalHeight();
  }
  this.setContainerTranslate(startIndex); // 设置内容容器偏移量
};
// 设置内容容器偏移量
VirtualList.prototype.setContainerTranslate = function (startIndex) {
  var offset =
    this.options.dynamicHeight === true // 根据是否处于动态高度模式来返回偏移结果
      ? this.listPositionsInfo[startIndex] && this.listPositionsInfo[startIndex].top
      : startIndex * this.options.listHeight;
  this.__offsetContainer.style.webkitTransform = 'translate3d(0, ' + offset + 'px,0)';
};
// 更新列表位置信息
VirtualList.prototype.updatelistPositionInfo = function (children, startIndex) {
  var index = startIndex;
  var _this = this;
  if (children.length > 0) {
    Array.prototype.forEach.call(children, function (childNode) {
      var rect = childNode.getBoundingClientRect();
      var height = rect.height;
      var previousHeight = _this.listPositionsInfo[index].height;
      var dValue = previousHeight - height; // 计算出差值
      if (dValue) {
        _this.listPositionsInfo[index].bottom =
          _this.listPositionsInfo[index].bottom - dValue;
        _this.listPositionsInfo[index].height = height;

        for (var i = index + 1; i < _this.listPositionsInfo.length; i++) {
          _this.listPositionsInfo[i].top = _this.listPositionsInfo[i - 1].bottom;
          _this.listPositionsInfo[i].bottom = _this.listPositionsInfo[i].bottom - dValue;
        }
      }
      index++;
    });
  }
};
// 更新列表总高度
VirtualList.prototype.updateTotalHeight = function () {
  if (this.listPositionsInfo && this.listPositionsInfo.length > 0) {
    this.__maskContainer.style.height =
      this.listPositionsInfo[this.listPositionsInfo.length - 1].bottom + 'px';
  }
};
